using System;
using System.Collections;
using UnityEngine;

[RequireComponent(typeof(CharacterController))]
[AddComponentMenu("Character/Character Motor")]
public class CharacterMotor : MonoBehaviour
{
	public bool canControl = true;

	public bool isEnabled = true;

	public float boostMults = 1f;

	public float maxForwardSpeed = 5f;

	public float maxSidewaysSpeed = 5f;

	public float maxBackwardsSpeed = 5f;

	public MotorPreset[] MotorPreset = new MotorPreset[1]
	{
		new MotorPreset()
	};

	public int MovementIndex;

	public bool useFixedUpdate = true;

	public AnimationCurve slopeSpeedMultiplier = new AnimationCurve(new Keyframe(-90f, 1f), new Keyframe(0f, 1f), new Keyframe(90f, 0f));

	public float maxGroundAcceleration = 30f;

	public float maxAirAcceleration = 20f;

	public float gravity = 10f;

	public float maxFallSpeed = 20f;

	[NonSerialized]
	public Vector3 inputMoveDirection = Vector3.zero;

	[NonSerialized]
	public bool inputJump;

	[NonSerialized]
	public bool climb;

	public CharacterMotorMovement movement = new CharacterMotorMovement();

	private CharacterMotorJumping jumping = new CharacterMotorJumping();

	private CharacterMotorMovingPlatform movingPlatform = new CharacterMotorMovingPlatform();

	private CharacterMotorSliding sliding = new CharacterMotorSliding();

	public bool grounded = true;

	[NonSerialized]
	public Vector3 groundNormal = Vector3.zero;

	private Vector3 lastGroundNormal = Vector3.zero;

	[HideInInspector]
	public CharacterController controller;

	public int layerMask = 8;

	private bool ongroundtest;

	public void Reset()
	{
		inputJump = false;
		inputMoveDirection = Vector3.zero;
	}

	private void Awake()
	{
		controller = base.gameObject.GetComponent<CharacterController>();
	}

	public void Climb(float speed)
	{
		climb = true;
		controller.transform.position += Vector3.up * speed * Time.deltaTime;
	}

	private void UpdateFunction()
	{
		if (!base.enabled)
		{
			return;
		}
		Vector3 position = base.transform.position;
		if (MotorPreset.Length > MovementIndex && controller != null && controller.gameObject.activeSelf && controller.enabled)
		{
			controller.height = MotorPreset[MovementIndex].Height;
			controller.center = MotorPreset[MovementIndex].Center;
			controller.radius = MotorPreset[MovementIndex].Redius;
		}
		if (isEnabled)
		{
			if (!(controller != null) || !controller.gameObject.activeSelf || !controller.enabled)
			{
				return;
			}
			Vector3 velocity = movement.velocity;
			velocity = ApplyInputVelocityChange(velocity);
			velocity = ApplyGravityAndJumping(velocity);
			Vector3 zero = Vector3.zero;
			if (MoveWithPlatform())
			{
				Vector3 a = movingPlatform.activePlatform.TransformPoint(movingPlatform.activeLocalPoint);
				zero = a - movingPlatform.activeGlobalPoint;
				if (zero != Vector3.zero)
				{
					controller.Move(zero);
				}
				Quaternion lhs = movingPlatform.activePlatform.rotation * movingPlatform.activeLocalRotation;
				Vector3 eulerAngles = (lhs * Quaternion.Inverse(movingPlatform.activeGlobalRotation)).eulerAngles;
				float y = eulerAngles.y;
				if (y != 0f)
				{
					base.transform.Rotate(0f, y, 0f);
				}
			}
			Vector3 vector = velocity * Time.deltaTime;
			float d = Mathf.Max(controller.stepOffset, new Vector3(vector.x, 0f, vector.z).magnitude);
			if (grounded)
			{
				vector -= d * Vector3.up;
			}
			if (climb)
			{
				vector.y = 0f;
				float num = Vector3.Dot(base.transform.forward, velocity.normalized);
				if (num < 0f && !ongroundtest)
				{
					vector.x = 0f;
					vector.z = 0f;
				}
			}
			movingPlatform.hitPlatform = null;
			groundNormal = Vector3.zero;
			movement.collisionFlags = controller.Move(vector);
			movement.lastHitPoint = movement.hitPoint;
			lastGroundNormal = groundNormal;
			if (movingPlatform.enabled && movingPlatform.activePlatform != movingPlatform.hitPlatform && movingPlatform.hitPlatform != null)
			{
				movingPlatform.activePlatform = movingPlatform.hitPlatform;
				movingPlatform.lastMatrix = movingPlatform.hitPlatform.localToWorldMatrix;
				movingPlatform.newPlatform = true;
			}
			Vector3 vector2 = new Vector3(velocity.x, 0f, velocity.z);
			movement.velocity = (base.transform.position - position) / Time.deltaTime;
			Vector3 lhs2 = new Vector3(movement.velocity.x, 0f, movement.velocity.z);
			if (vector2 == Vector3.zero)
			{
				movement.velocity = new Vector3(0f, movement.velocity.y, 0f);
			}
			else
			{
				float value = Vector3.Dot(lhs2, vector2) / vector2.sqrMagnitude;
				movement.velocity = vector2 * Mathf.Clamp01(value) + movement.velocity.y * Vector3.up;
			}
			if ((double)movement.velocity.y < (double)velocity.y - 0.001)
			{
				if (movement.velocity.y < 0f)
				{
					movement.velocity.y = velocity.y;
				}
				else
				{
					jumping.holdingJumpButton = false;
				}
			}
			if (grounded && !IsGroundedTest())
			{
				grounded = false;
				if (movingPlatform.enabled && (movingPlatform.movementTransfer == MovementTransferOnJump.InitTransfer || movingPlatform.movementTransfer == MovementTransferOnJump.PermaTransfer))
				{
					movement.frameVelocity = movingPlatform.platformVelocity;
					movement.velocity += movingPlatform.platformVelocity;
				}
				SendMessage("OnFall", SendMessageOptions.DontRequireReceiver);
				base.transform.position += d * Vector3.up;
			}
			else if (!grounded && IsGroundedTest())
			{
				grounded = true;
				jumping.jumping = false;
				SubtractNewPlatformVelocity();
				SendMessage("OnLand", SendMessageOptions.DontRequireReceiver);
			}
			if (MoveWithPlatform())
			{
				CharacterMotorMovingPlatform characterMotorMovingPlatform = movingPlatform;
				Vector3 position2 = base.transform.position;
				Vector3 up = Vector3.up;
				Vector3 center = controller.center;
				characterMotorMovingPlatform.activeGlobalPoint = position2 + up * (center.y - controller.height * 0.5f + controller.radius);
				movingPlatform.activeLocalPoint = movingPlatform.activePlatform.InverseTransformPoint(movingPlatform.activeGlobalPoint);
				movingPlatform.activeGlobalRotation = base.transform.rotation;
				movingPlatform.activeLocalRotation = Quaternion.Inverse(movingPlatform.activePlatform.rotation) * movingPlatform.activeGlobalRotation;
			}
			ongroundtest = false;
			climb = false;
		}
		else
		{
			grounded = false;
			int num2 = 1 << layerMask;
			RaycastHit hitInfo;
			if (Physics.Raycast(base.transform.position + controller.center + Vector3.up * 0.01f, -Vector3.up, out hitInfo, controller.height / 2f + controller.stepOffset + 0.01f, num2) && (bool)hitInfo.collider && hitInfo.collider.gameObject.transform.root != base.gameObject.transform.root)
			{
				grounded = true;
			}
		}
	}

	private void FixedUpdate()
	{
		if (movingPlatform.enabled)
		{
			if (movingPlatform.activePlatform != null)
			{
				if (!movingPlatform.newPlatform)
				{
					movingPlatform.platformVelocity = (movingPlatform.activePlatform.localToWorldMatrix.MultiplyPoint3x4(movingPlatform.activeLocalPoint) - movingPlatform.lastMatrix.MultiplyPoint3x4(movingPlatform.activeLocalPoint)) / Time.deltaTime;
				}
				movingPlatform.lastMatrix = movingPlatform.activePlatform.localToWorldMatrix;
				movingPlatform.newPlatform = false;
			}
			else
			{
				movingPlatform.platformVelocity = Vector3.zero;
			}
		}
		if (useFixedUpdate)
		{
			UpdateFunction();
		}
	}

	private void Update()
	{
		if (!useFixedUpdate)
		{
			UpdateFunction();
		}
	}

	private Vector3 ApplyInputVelocityChange(Vector3 velocity)
	{
		if (!canControl)
		{
			inputMoveDirection = Vector3.zero;
		}
		Vector3 vector;
		if (grounded && TooSteep())
		{
			vector = new Vector3(groundNormal.x, 0f, groundNormal.z).normalized;
			Vector3 vector2 = Vector3.Project(inputMoveDirection, vector);
			vector = vector + vector2 * sliding.speedControl + (inputMoveDirection - vector2) * sliding.sidewaysControl;
			vector *= sliding.slidingSpeed;
		}
		else
		{
			vector = GetDesiredHorizontalVelocity();
		}
		if (movingPlatform.enabled && movingPlatform.movementTransfer == MovementTransferOnJump.PermaTransfer)
		{
			vector += movement.frameVelocity * 10f;
			vector.y = 0f;
		}
		if (grounded)
		{
			vector = AdjustGroundVelocityToNormal(vector, groundNormal);
		}
		else
		{
			velocity.y = 0f;
		}
		float num = GetMaxAcceleration(grounded) * Time.deltaTime;
		Vector3 b = vector - velocity;
		if (b.sqrMagnitude > num * num)
		{
			b = b.normalized * num;
		}
		if ((double)Vector3.Dot(vector.normalized, velocity.normalized) < 0.9)
		{
			velocity = Vector3.Lerp(velocity, vector, 0.5f);
		}
		if (grounded || canControl)
		{
			velocity += b;
		}
		if (grounded)
		{
			velocity.y = Mathf.Min(velocity.y, 0f);
		}
		return velocity;
	}

	private Vector3 ApplyGravityAndJumping(Vector3 velocity)
	{
		if (climb)
		{
			return velocity;
		}
		if (!inputJump || !canControl)
		{
			jumping.holdingJumpButton = false;
			jumping.lastButtonDownTime = -100f;
		}
		if (inputJump && jumping.lastButtonDownTime < 0f && canControl)
		{
			jumping.lastButtonDownTime = Time.time;
		}
		if (grounded)
		{
			velocity.y = Mathf.Min(0f, velocity.y) - gravity * Time.deltaTime;
		}
		else
		{
			velocity.y = movement.velocity.y - gravity * Time.deltaTime * 2f;
			if (jumping.jumping && jumping.holdingJumpButton && Time.time < jumping.lastStartTime + jumping.extraHeight / CalculateJumpVerticalSpeed(jumping.baseHeight))
			{
				velocity += jumping.jumpDir * gravity * Time.deltaTime;
			}
			velocity.y = Mathf.Max(velocity.y, 0f - maxFallSpeed);
		}
		if (grounded)
		{
			if (jumping.enabled && canControl && (double)(Time.time - jumping.lastButtonDownTime) < 0.2)
			{
				grounded = false;
				jumping.jumping = true;
				jumping.lastStartTime = Time.time;
				jumping.lastButtonDownTime = -100f;
				jumping.holdingJumpButton = true;
				if (TooSteep())
				{
					jumping.jumpDir = Vector3.Slerp(Vector3.up, groundNormal, jumping.steepPerpAmount);
				}
				else
				{
					jumping.jumpDir = Vector3.Slerp(Vector3.up, groundNormal, jumping.perpAmount);
				}
				velocity.y = 0f;
				velocity += jumping.jumpDir * CalculateJumpVerticalSpeed(jumping.baseHeight);
				if (movingPlatform.enabled && (movingPlatform.movementTransfer == MovementTransferOnJump.InitTransfer || movingPlatform.movementTransfer == MovementTransferOnJump.PermaTransfer))
				{
					movement.frameVelocity = movingPlatform.platformVelocity;
					velocity += movingPlatform.platformVelocity;
				}
				SendMessage("OnJump", SendMessageOptions.DontRequireReceiver);
			}
			else
			{
				jumping.holdingJumpButton = false;
			}
		}
		return velocity;
	}

	private void OnControllerColliderHit(ControllerColliderHit hit)
	{
		Vector3 normal = hit.normal;
		if (!(normal.y > 0f))
		{
			return;
		}
		Vector3 normal2 = hit.normal;
		if (!(normal2.y > groundNormal.y))
		{
			return;
		}
		Vector3 moveDirection = hit.moveDirection;
		if (moveDirection.y < 0f)
		{
			if ((double)(hit.point - movement.lastHitPoint).sqrMagnitude > 0.001 || lastGroundNormal == Vector3.zero)
			{
				groundNormal = hit.normal;
			}
			else
			{
				groundNormal = lastGroundNormal;
			}
			movingPlatform.hitPlatform = hit.collider.transform;
			movement.hitPoint = hit.point;
			movement.frameVelocity = Vector3.zero;
			ongroundtest = true;
		}
	}

	private IEnumerable SubtractNewPlatformVelocity()
	{
		if (!movingPlatform.enabled || (movingPlatform.movementTransfer != MovementTransferOnJump.InitTransfer && movingPlatform.movementTransfer != MovementTransferOnJump.PermaTransfer))
		{
			yield break;
		}
		if (movingPlatform.newPlatform)
		{
			Transform platform = movingPlatform.activePlatform;
			yield return new WaitForFixedUpdate();
			yield return new WaitForFixedUpdate();
			if (grounded && platform == movingPlatform.activePlatform)
			{
				yield return 1;
			}
		}
		movement.velocity -= movingPlatform.platformVelocity;
	}

	private bool MoveWithPlatform()
	{
		return movingPlatform.enabled && (grounded || movingPlatform.movementTransfer == MovementTransferOnJump.PermaLocked) && movingPlatform.activePlatform != null;
	}

	private Vector3 GetDesiredHorizontalVelocity()
	{
		Vector3 vector = base.transform.InverseTransformDirection(inputMoveDirection);
		float num = MaxSpeedInDirection(vector);
		if (grounded)
		{
			Vector3 normalized = movement.velocity.normalized;
			float time = Mathf.Asin(normalized.y) * 57.29578f;
			num *= slopeSpeedMultiplier.Evaluate(time);
		}
		return base.transform.TransformDirection(vector * num);
	}

	private Vector3 AdjustGroundVelocityToNormal(Vector3 hVelocity, Vector3 groundNormal)
	{
		Vector3 lhs = Vector3.Cross(Vector3.up, hVelocity);
		return Vector3.Cross(lhs, groundNormal).normalized * hVelocity.magnitude;
	}

	public bool IsGroundedTest()
	{
		return (double)groundNormal.y > 0.01;
	}

	private float GetMaxAcceleration(bool grounded)
	{
		return (!grounded) ? maxAirAcceleration : maxGroundAcceleration;
	}

	private float CalculateJumpVerticalSpeed(float targetJumpHeight)
	{
		return Mathf.Sqrt(2f * targetJumpHeight * gravity);
	}

	private bool IsJumping()
	{
		return jumping.jumping;
	}

	private bool IsSliding()
	{
		return grounded && sliding.enabled && TooSteep();
	}

	private bool IsTouchingCeiling()
	{
		return (movement.collisionFlags & CollisionFlags.Above) != CollisionFlags.None;
	}

	public bool IsGrounded()
	{
		return grounded;
	}

	private bool TooSteep()
	{
		return groundNormal.y <= Mathf.Cos(controller.slopeLimit * ((float)Math.PI / 180f));
	}

	private Vector3 GetDirection()
	{
		return inputMoveDirection;
	}

	private void SetControllable(bool controllable)
	{
		canControl = controllable;
	}

	private float MaxSpeedInDirection(Vector3 desiredMovementDirection)
	{
		if (desiredMovementDirection == Vector3.zero)
		{
			return 0f;
		}
		float num = ((!(desiredMovementDirection.z > 0f)) ? (maxBackwardsSpeed * boostMults) : (maxForwardSpeed * boostMults)) / maxSidewaysSpeed * boostMults;
		Vector3 normalized = new Vector3(desiredMovementDirection.x, 0f, desiredMovementDirection.z / num).normalized;
		return new Vector3(normalized.x, 0f, normalized.z * num).magnitude * maxSidewaysSpeed * boostMults;
	}

	private void SetVelocitys(Vector3 velocity)
	{
		grounded = false;
		movement.velocity = velocity;
		movement.frameVelocity = Vector3.zero;
		SendMessage("OnExternalVelocity");
	}
}
